--! This file is part of the FELIX firmware distribution (https://gitlab.cern.ch/atlas-tdaq-felix/firmware/).
--! Copyright (C) 2001-2021 CERN for the benefit of the ATLAS collaboration.
--! Authors:
--!               Andrea Borga
--!               Israel Grayzman
--!               Kai Chen
--!               Enrico Gamberini
--!               RHabraken
--!               Rene
--!               Frans Schreuder
--!
--!   Licensed under the Apache License, Version 2.0 (the "License");
--!   you may not use this file except in compliance with the License.
--!   You may obtain a copy of the License at
--!
--!       http://www.apache.org/licenses/LICENSE-2.0
--!
--!   Unless required by applicable law or agreed to in writing, software
--!   distributed under the License is distributed on an "AS IS" BASIS,
--!   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--!   See the License for the specific language governing permissions and
--!   limitations under the License.



library ieee, UNISIM;
    use ieee.numeric_std.all;
    use UNISIM.VCOMPONENTS.all;
    use ieee.numeric_std_unsigned.all;
    use ieee.std_logic_1164.all;
    use work.pcie_package.all;
    use work.I2C.all;

library xpm;
    use xpm.vcomponents.all;

entity housekeeping_module is
    generic(
        CARD_TYPE                       : integer := 710;
        ENDPOINTS                       : integer := 1; --Number of PCIe Endpoints in the design, GBT_NUM has to be multiplied by this number in some cases.
        BLOCKSIZE                       : integer := 1024;
        DATA_WIDTH                      : integer := 256;
        USE_VERSAL_CPM                  : boolean := false
    );
  port (
        SCL                         : inout  std_logic;
        SDA                         : inout  std_logic;
        appreg_clk                  : in     std_logic;
        i2cmux_rst                  : out    std_logic;
        leds                        : out    std_logic_vector(NUM_LEDS(CARD_TYPE)-1 downto 0); --! 8 status leds
        register_map_control        : in     register_map_control_type;
        register_map_gen_board_info : out    register_map_gen_board_info_type;
        register_map_hk_monitor     : out    register_map_hk_monitor_type;
        rst_soft                    : in     std_logic;
        sys_reset_n                 : in     std_logic;

        
        
        versal_sys_reset_n_out      : out    std_logic;
        WupperToCPM: in WupperToCPM_array_type(0 to 1);
        CPMToWupper: out CPMToWupper_array_type(0 to 1);
        clk100_out : out std_logic;
        DDR_in : in DDR_in_array_type(0 to NUM_DDR(CARD_TYPE)-1);
        DDR_out : out DDR_out_array_type(0 to NUM_DDR(CARD_TYPE)-1);
        DDR_inout : inout DDR_inout_array_type(0 to NUM_DDR(CARD_TYPE)-1);
        LPDDR_in : in LPDDR_in_array_type(0 to NUM_LPDDR(CARD_TYPE)/2-1);
        LPDDR_out : out LPDDR_out_array_type(0 to NUM_LPDDR(CARD_TYPE)-1);
        LPDDR_inout : inout LPDDR_inout_array_type(0 to NUM_LPDDR(CARD_TYPE)-1)
    );
end entity housekeeping_module;


architecture structure of housekeeping_module is

    signal RST                            : std_logic;
    signal nReset                         : std_logic;
    signal clk                            : std_logic;
    signal cmd_ack                        : std_logic;
    --signal ack_out                        : std_logic;
    signal Dout                           : std_logic_vector(7 downto 0);
    signal Din                            : std_logic_vector(7 downto 0);
    signal ack_in                         : std_logic;
    signal write                          : std_logic;
    signal read                           : std_logic;
    signal stop                           : std_logic;
    signal start                          : std_logic;
    signal ena                            : std_logic;
    signal reset                          : std_logic;
    signal AUTOMATIC_CLOCK_SWITCH_ENABLED : std_logic_vector(0 downto 0);
    signal TACH_CNT                       : std_logic_vector(19 downto 0);
    signal TACH_CNT_LATCHED               : std_logic_vector(19 downto 0);
    signal TACH_FLAG                      : std_logic:='0';
    signal TACH_R                         : std_logic:='0';
    signal TACH_2R                        : std_logic:='0';
    signal TACH_3R                        : std_logic:='0';

    signal dna_out_data                   : std_logic_vector(95 downto 0); --IG: get the entire output vector data and assign the relevant bits only
    signal LMK_locked                     : std_logic;
    signal gpio2_o                        : std_logic_vector(20 downto 0);
    signal gpio_o                         : std_logic_vector(20 downto 0);
    signal gpio2_i                        : std_logic_vector(20 downto 0);
    signal gpio_i                         : std_logic_vector(20 downto 0);

    signal scl_cips_o                     : std_logic := '1';
    signal sda_cips_o                     : std_logic := '1';
    signal scl_opencores_o                : std_logic := '1';
    signal sda_opencores_o                : std_logic := '1';
    signal sda_opencores_cips_clk_o       : std_logic := '1';
    signal scl_opencores_cips_clk_o       : std_logic := '1';
    signal SDA_i                          : std_logic := '1';
    signal SCL_i                          : std_logic := '1';
    signal SDA_o                          : std_logic := '1';
    signal SCL_o                          : std_logic := '1';
    --attribute IOB                         : string;
    --attribute IOB of SDA_o, SCL_o         : signal is "TRUE";

    signal pl0_ref_clk                    : std_logic;

begin



    i2c0: entity work.simple_i2c
        port map(
            clk     => clk,
            ena     => ena,
            nReset  => nReset,
            clk_cnt => "01100100",
            start   => start,
            stop    => stop,
            read    => read,
            write   => write,
            ack_in  => ack_in,
            Din     => Din,
            cmd_ack => cmd_ack,
            ack_out => open, --ack_out,
            Dout    => Dout,
            SCL_i    => SCL_i,
            SDA_i    => SDA_i,
            SCL_o    => scl_opencores_o,
            SDA_o    => sda_opencores_o);

    SCL_iobuf: IOBUF  -- @suppress "Generic map uses default values. Missing optional actuals: DRIVE, IBUF_LOW_PWR, IOSTANDARD, SLEW"
        port map(
            O => SCL_i,
            IO => SCL,
            I => '0',
            T => SCL_o
        );

    SDA_iobuf: IOBUF  -- @suppress "Generic map uses default values. Missing optional actuals: DRIVE, IBUF_LOW_PWR, IOSTANDARD, SLEW"
        port map(
            O => SDA_i,
            IO => SDA,
            I => '0',
            T => SDA_o
        );


    g_182_i2c: if (CARD_TYPE = 182 or CARD_TYPE = 155 or CARD_TYPE = 120) generate
    begin
        xpm_cdc_sda : xpm_cdc_single
            generic map(
                DEST_SYNC_FF => 2,
                INIT_SYNC_FF => 0,
                SIM_ASSERT_CHK => 0,
                SRC_INPUT_REG => 1
            )

            port map(
                src_clk => clk,
                src_in => sda_opencores_o,
                dest_clk => pl0_ref_clk,
                dest_out => sda_opencores_cips_clk_o
            );

        xpm_cdc_scl : xpm_cdc_single
            generic map(
                DEST_SYNC_FF => 2,
                INIT_SYNC_FF => 0,
                SIM_ASSERT_CHK => 0,
                SRC_INPUT_REG => 1
            )

            port map(
                src_clk => clk,
                src_in => scl_opencores_o,
                dest_clk => pl0_ref_clk,
                dest_out => scl_opencores_cips_clk_o
            );

        process (pl0_ref_clk)
        begin
            if rising_edge(pl0_ref_clk) then
                SDA_o  <=  sda_opencores_cips_clk_o and sda_cips_o;
                SCL_o  <=  scl_opencores_cips_clk_o and scl_cips_o;
            end if;
        end process;

    else generate -- g_182_i2c
        process (clk)
        begin
            if rising_edge(clk) then
                SDA_o <= sda_opencores_o;
                SCL_o <= scl_opencores_o;
            end if;
        end process;

        scl_cips_o <= '1';
        sda_cips_o <= '1';
    end generate; -- g_182_i2c


    i2cint0: entity work.i2c_interface
        port map(
            Din                  => Din,
            Dout                 => Dout,
            I2C_RD               => register_map_hk_monitor.I2C_RD,
            I2C_WR               => register_map_hk_monitor.I2C_WR,
            RST                  => RST,
            ack_in               => ack_in,
            --ack_out              => ack_out,
            appreg_clk           => appreg_clk,
            clk                  => clk,
            cmd_ack              => cmd_ack,
            ena                  => ena,
            nReset               => nReset,
            read                 => read,
            register_map_control => register_map_control,
            rst_soft             => rst_soft,
            start                => start,
            stop                 => stop,
            write                => write);
            
            register_map_gen_board_info.CARD_TYPE <= std_logic_vector(to_unsigned(CARD_TYPE,64)); 
            register_map_gen_board_info.NUMBER_OF_PCIE_ENDPOINTS <= std_logic_vector(to_unsigned(ENDPOINTS,2));

    xadc0: entity work.xadc_drp
        generic map(
            CARD_TYPE => CARD_TYPE)
        port map(
            clk40   => appreg_clk,
            reset   => reset,
            temp    => register_map_hk_monitor.FPGA_CORE_TEMP,
            vccint  => register_map_hk_monitor.FPGA_CORE_VCCINT,
            vccaux  => register_map_hk_monitor.FPGA_CORE_VCCAUX,
            vccbram => register_map_hk_monitor.FPGA_CORE_VCCBRAM);

    dna0: entity work.dna
        generic map(
            CARD_TYPE => CARD_TYPE)
        port map(
            clk40   => appreg_clk,
            reset   => reset,
            --IG      dna_out(63 downto 0) => register_map_hk_monitor.FPGA_DNA,
            --IG      dna_out(95 downto 64) => open);
            dna_out => dna_out_data);
    register_map_hk_monitor.FPGA_DNA  <= dna_out_data(63 downto 0);

    g_180: if (CARD_TYPE = 180) generate
        component cips_bd_wrapper
            port (
                pl0_resetn : out STD_LOGIC;
                pl0_ref_clk_0 : out std_logic
            );
        end component;
    begin
        versal_cips_block0: cips_bd_wrapper
            port map(
                pl0_resetn => versal_sys_reset_n_out,
                pl0_ref_clk_0 => clk100_out
            );
    end generate g_180;

    g_181: if (CARD_TYPE = 181) generate
        component cips_bd_BNL181_wrapper
            port (
                pl0_resetn : out STD_LOGIC
            );
        end component;
    begin
        versal_cips_block0: cips_bd_BNL181_wrapper
            port map(
                pl0_resetn => versal_sys_reset_n_out
            );
    end generate g_181;


    g_182_nocpm: if (CARD_TYPE = 182 and not USE_VERSAL_CPM) generate
        component cips_bd_BNL182_wrapper
            port (
                CH0_DDR4_0_0_act_n : out STD_LOGIC_VECTOR ( 0 to 0 );
                CH0_DDR4_0_0_adr : out STD_LOGIC_VECTOR ( 16 downto 0 );
                CH0_DDR4_0_0_ba : out STD_LOGIC_VECTOR ( 1 downto 0 );
                CH0_DDR4_0_0_bg : out STD_LOGIC_VECTOR ( 1 downto 0 );
                CH0_DDR4_0_0_ck_c : out STD_LOGIC_VECTOR ( 1 downto 0 );
                CH0_DDR4_0_0_ck_t : out STD_LOGIC_VECTOR ( 1 downto 0 );
                CH0_DDR4_0_0_cke : out STD_LOGIC_VECTOR ( 1 downto 0 );
                CH0_DDR4_0_0_cs_n : out STD_LOGIC_VECTOR ( 1 downto 0 );
                CH0_DDR4_0_0_dm_n : inout STD_LOGIC_VECTOR ( 7 downto 0 );
                CH0_DDR4_0_0_dq : inout STD_LOGIC_VECTOR ( 63 downto 0 );
                CH0_DDR4_0_0_dqs_c : inout STD_LOGIC_VECTOR ( 7 downto 0 );
                CH0_DDR4_0_0_dqs_t : inout STD_LOGIC_VECTOR ( 7 downto 0 );
                CH0_DDR4_0_0_odt : out STD_LOGIC_VECTOR ( 1 downto 0 );
                CH0_DDR4_0_0_reset_n : out STD_LOGIC_VECTOR ( 0 to 0 );
                scl_i : in STD_LOGIC;
                scl_o : out STD_LOGIC;
                sda_i : in STD_LOGIC;
                sda_o : out STD_LOGIC;
                clk100_out : out STD_LOGIC;
                gpio2_io_i_0 : in STD_LOGIC_VECTOR ( 20 downto 0 );
                gpio2_io_o_0 : out STD_LOGIC_VECTOR ( 20 downto 0 );
                gpio_io_i_0 : in STD_LOGIC_VECTOR ( 20 downto 0 );
                gpio_io_o_0 : out STD_LOGIC_VECTOR ( 20 downto 0 );
                pl0_resetn : out STD_LOGIC_VECTOR ( 0 to 0 );
                sys_clk0_0_clk_n : in STD_LOGIC_VECTOR ( 0 to 0 );
                sys_clk0_0_clk_p : in STD_LOGIC_VECTOR ( 0 to 0 );
                pl0_ref_clk : out STD_LOGIC
            );
        end component;
    begin
        versal_cips_block0: cips_bd_BNL182_wrapper
            port map(
                pl0_resetn(0) => versal_sys_reset_n_out,
                clk100_out => clk100_out,
                CH0_DDR4_0_0_act_n   => DDR_out(0).act_n,
                CH0_DDR4_0_0_adr     => DDR_out(0).adr,
                CH0_DDR4_0_0_ba      => DDR_out(0).ba,
                CH0_DDR4_0_0_bg      => DDR_out(0).bg,
                CH0_DDR4_0_0_ck_c    => DDR_out(0).ck_c,
                CH0_DDR4_0_0_ck_t    => DDR_out(0).ck_t,
                CH0_DDR4_0_0_cke     => DDR_out(0).cke,
                CH0_DDR4_0_0_cs_n    => DDR_out(0).cs_n,
                CH0_DDR4_0_0_dm_n    => DDR_inout(0).dm_n(7 downto 0),
                CH0_DDR4_0_0_dq      => DDR_inout(0).dq(63 downto 0),
                CH0_DDR4_0_0_dqs_c   => DDR_inout(0).dqs_c(7 downto 0),
                CH0_DDR4_0_0_dqs_t   => DDR_inout(0).dqs_t(7 downto 0),
                CH0_DDR4_0_0_odt     => DDR_out(0).odt,
                CH0_DDR4_0_0_reset_n => DDR_out(0).reset_n,
                gpio2_io_o_0         => gpio2_o,
                gpio_io_o_0          => gpio_o,
                gpio2_io_i_0         => gpio2_i,
                gpio_io_i_0          => gpio_i,
                scl_i                => SCL_i,
                sda_i                => SDA_i,
                scl_o                => scl_cips_o,
                sda_o                => sda_cips_o,
                sys_clk0_0_clk_n     => DDR_in(0).sys_clk_n,
                sys_clk0_0_clk_p     => DDR_in(0).sys_clk_p,
                pl0_ref_clk          => pl0_ref_clk
            );
    end generate g_182_nocpm;

    g_182cpm: if (CARD_TYPE = 182  and USE_VERSAL_CPM) or CARD_TYPE = 155 or CARD_TYPE = 120 generate
        component versal_cpm_pcie_endpoints
            generic(CARD_TYPE : integer);
            port(
                DDR_in       : in    DDR_in_array_type(0 to NUM_DDR(CARD_TYPE)-1);
                DDR_out      : out   DDR_out_array_type(0 to NUM_DDR(CARD_TYPE)-1);
                DDR_inout    : inout DDR_inout_array_type(0 to NUM_DDR(CARD_TYPE)-1);
                LPDDR_in     : in    LPDDR_in_array_type(0 to NUM_LPDDR(CARD_TYPE)/2-1);
                LPDDR_out    : out   LPDDR_out_array_type(0 to NUM_LPDDR(CARD_TYPE)-1);
                LPDDR_inout  : inout LPDDR_inout_array_type(0 to NUM_LPDDR(CARD_TYPE)-1);
                scl_i                : in    STD_LOGIC;
                scl_o                : out   STD_LOGIC;
                sda_i                : in    STD_LOGIC;
                sda_o                : out   STD_LOGIC;
                gpio2_io_i_0         : in    STD_LOGIC_VECTOR ( 20 downto 0 );
                gpio2_io_o_0         : out   STD_LOGIC_VECTOR ( 20 downto 0 );
                gpio_io_i_0          : in    STD_LOGIC_VECTOR ( 20 downto 0 );
                gpio_io_o_0          : out   STD_LOGIC_VECTOR ( 20 downto 0 );
                pl0_resetn           : out   STD_LOGIC_VECTOR ( 0 to 0 );
                pl0_ref_clk          : out   STD_LOGIC;
                WupperToCPM          : in    WupperToCPM_array_type(0 to 1);
                CPMToWupper          : out   CPMToWupper_array_type(0 to 1);
                clk100_out           : out   std_logic
            );
        end component versal_cpm_pcie_endpoints;
    begin
        cpm0: versal_cpm_pcie_endpoints
            generic map(
                CARD_TYPE => CARD_TYPE
            )
            Port map(

                gpio2_io_o_0         => gpio2_o,
                gpio_io_o_0          => gpio_o,
                gpio2_io_i_0         => gpio2_i,
                gpio_io_i_0          => gpio_i,
                scl_i                => SCL_i,
                sda_i                => SDA_i,
                scl_o                => scl_cips_o,
                sda_o                => sda_cips_o,
                pl0_resetn(0) => versal_sys_reset_n_out,
                pl0_ref_clk => pl0_ref_clk,
                WupperToCPM => WupperToCPM,
                CPMToWupper => CPMToWupper,
                clk100_out => clk100_out,
                DDR_out => DDR_out,
                DDR_inout => DDR_inout,
                LPDDR_out => LPDDR_out,
                LPDDR_inout => LPDDR_inout,
                DDR_in => DDR_in,
                LPDDR_in => LPDDR_in
            );
    else generate

    end generate;



    reset <= rst_soft or RST;

    RST <= not sys_reset_n;


    g_FLX182_155: if CARD_TYPE = 182 or CARD_TYPE = 155 generate
        signal GPIO_SEL : std_logic;
        signal GPIO_LEDS: std_logic_vector(3 downto 0);

    begin
        GPIO_SEL <= gpio_o(20);
        GPIO_LEDS <= gpio_o(4) & gpio_o(0) & gpio_o(2) & gpio_o(3);
        
        with GPIO_SEL select
            leds <= register_map_control.STATUS_LEDS(NUM_LEDS(CARD_TYPE)-1 downto 0) when '0',
                GPIO_LEDS(NUM_LEDS(CARD_TYPE)-1 downto 0) when others;


        gpio_i(7) <= sys_reset_n;



    else generate
        
        leds <= register_map_control.STATUS_LEDS(NUM_LEDS(CARD_TYPE)-1 downto 0);

    end generate;


    


end architecture structure ; -- of housekeeping_module

